{{>licenseInfo}}

// generated from openapiRoute.mustache
package {{apiPackage}}

import cask.model.Response

import java.nio.file.{Files, Path, Paths}

/**
 * This code will try and download the swagger UI static files on startup
 *
 * That behaviour can be altered by:
 *  - setting the environment variable SWAGGER_ON to false
 *  - setting the environment variable SWAGGER_UI_URL to either the URL of a swagger UI zip or setting it to the empty string
 *
 */
object OpenApiRoutes {

  def swaggerUIUrl: Option[String] = {
    // flag to turn SWAGGER off
    def useSwaggerUI = sys.env.get("SWAGGER_ON").map(_.toBoolean).getOrElse(true)

    val defaultUrl = "https://github.com/swagger-api/swagger-ui/archive/refs/tags/v5.11.9.zip"
    Option(sys.env.getOrElse("SWAGGER_UI_URL", defaultUrl))
      .map(_.trim)
      .filterNot(_.isEmpty)
      .filter(_ => useSwaggerUI)
  }
  def apply(localPort: Int) = new OpenApiRoutes(localPort, swaggerUIUrl)
}

class OpenApiRoutes(localPort: Int, swaggerUrl: Option[String]) extends cask.Routes {

  def openApiDir = "ui"

  @cask.get("/")
  def index() = cask.Redirect("/ui/index.html")

  @cask.staticFiles("/ui")
  def staticUI() = openApiDir

  @cask.staticResources("/openapi.json")
  def staticOpenApi() = "openapi.json"

  /** This code will try and download the swagger UI artefacts to a local directory to serve up
    */
  object extract {

    def openApiDirPath: Path = Paths.get(openApiDir)
    def hasSwagger           = Files.exists(openApiDirPath) && Files.isDirectory(openApiDirPath)

    import java.io.{BufferedInputStream, FileOutputStream, InputStream}
    import java.net.URL
    import java.util.zip.{ZipEntry, ZipInputStream}
    import scala.util.Using

    def apply(url: String) = {
      if !hasSwagger then downloadAndExtractZip(url, openApiDir)
    }

    def downloadAndExtractZip(url: String, outputDir: String): Unit = {
      val urlConn = new URL(url).openConnection()
      urlConn.setRequestProperty("User-Agent", "Mozilla/5.0")

      Using(urlConn.getInputStream) { inputStream =>
        val zipIn = new ZipInputStream(new BufferedInputStream(inputStream))
        LazyList.continually(zipIn.getNextEntry).takeWhile(_ != null).foreach { entry =>

          def isDist   = entry.getName.contains("/dist/")
          def isNotMap = !entry.getName.endsWith(".map")

          if (!entry.isDirectory && isDist && isNotMap) {
            val fileName = entry.getName.split("/").last
            extractFile(entry.getName, zipIn, s"$outputDir/$fileName")
          }
          zipIn.closeEntry()
        }
      }
    }

    def extractFile(name: String, zipIn: ZipInputStream, filePath: String): Unit = {
      val fullPath = Paths.get(filePath).toAbsolutePath
      if !Files.exists(fullPath.getParent) then {
        Files.createDirectories(fullPath.getParent)
      }

      // config hack - we replace the default url from this swagger conf to use our localhost
      //
      if name.endsWith("swagger-initializer.js") then {
        val textLines = scala.io.Source.fromInputStream(zipIn).getLines().map {
          case line if line.contains("url:") =>
            s"""    url: "http://localhost:$localPort/openapi.json","""
          case line => line
        }

        // keeping this compatible for java 8, where this is from >= java 11:
        // Files.writeString(fullPath, textLines.mkString("\n"))
        scala.util.Using(new java.io.PrintWriter(fullPath.toFile))(_.write(textLines.mkString("\n")))
      } else {
        Using(new FileOutputStream(filePath)) { outputStream =>
          val buffer = new Array[Byte](1024)
          LazyList
            .continually(zipIn.read(buffer))
            .takeWhile(_ != -1)
            .foreach(outputStream.write(buffer, 0, _))
        }
      }
    }
  }

  // extract the swagger UI resources to our local directory
  swaggerUrl.foreach(url => extract(url))

  initialize()
}
